package com.intersult.nutils.net;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

public class SSL {
	public static class SavingTrustManager implements X509TrustManager {
		private final X509TrustManager trustManager;
		private X509Certificate[] certificates;
	
		SavingTrustManager(X509TrustManager trustManager) {
			this.trustManager = trustManager;
		}
	
		@Override
		public X509Certificate[] getAcceptedIssuers() {
			throw new UnsupportedOperationException();
		}
		@Override
		public void checkClientTrusted(X509Certificate[] chain, String authType) {
			throw new UnsupportedOperationException();
		}
		@Override
		public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
			this.certificates = chain;
			trustManager.checkServerTrusted(chain, authType);
		}
		
		public X509Certificate[] getCertificates() {
			return certificates;
		}
	}
	
	private SavingTrustManager trustManager;
	private SSLSocketFactory socketFactory;
	private KeyStore keyStore;
	private final String keyStoreKey;

	public SSL() throws Exception {
		this(null);
	}
	public SSL(String keyStoreKey) throws Exception {
		this.keyStoreKey = keyStoreKey == null ? "changeit" : keyStoreKey;
		keyStore = readKeyStore(this.keyStoreKey);
		SSLContext context = SSLContext.getInstance("TLS");
		TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
		trustFactory.init(keyStore);
		X509TrustManager defaultTrustManager = (X509TrustManager)trustFactory.getTrustManagers()[0];
		trustManager = new SSL.SavingTrustManager(defaultTrustManager);
		context.init(null, new TrustManager[] {trustManager}, null);
		socketFactory = context.getSocketFactory();
	}

	public X509Certificate[] getCertificates() {
		return trustManager.getCertificates();
	}
	
	public KeyStore getKeyStore() {
		return keyStore;
	}
	
	public SSLSocket createSocket(InetSocketAddress socketAddress) throws Exception {
		return (SSLSocket)socketFactory.createSocket(socketAddress.getHostName(), socketAddress.getPort());
	}

	public void readKeyStore() throws Exception, KeyStoreException, NoSuchAlgorithmException, CertificateException,
			IOException {
		keyStore = readKeyStore(keyStoreKey);
	}
	public void writeKeyStore() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
		writeKeyStore(keyStore, keyStoreKey);
	}
	
	public static KeyStore readKeyStore(String keyStoreKey) throws FileNotFoundException, KeyStoreException,
			IOException, NoSuchAlgorithmException, CertificateException {
		InputStream inputStream = new FileInputStream(getKeyStoreFile());
		KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
		keyStore.load(inputStream, keyStoreKey.toCharArray());
		inputStream.close();
		return keyStore;
	}
	public static void writeKeyStore(KeyStore keyStore, String keyStoreKey) throws KeyStoreException,
			NoSuchAlgorithmException, CertificateException, IOException {
		OutputStream out = new FileOutputStream(getKeyStoreFile());
		keyStore.store(out, keyStoreKey.toCharArray());
		out.close();
	}
	
	public static File getKeyStoreFile() {
		File file = new File("jssecacerts");
		if (!file.exists() || !file.isFile()) {
			File dir = new File(System.getProperty("java.home") + File.separatorChar + "lib" + File.separatorChar +
				"security");
			file = new File(dir, "jssecacerts");
			if (!file.exists() || !file.isFile())
				file = new File(dir, "cacerts");
		}
		return file;
	}
	
	public static void disableCertificates() {
		TrustManager[] trustAllCerts = new TrustManager[] {
			new X509TrustManager() {
				@Override
				public X509Certificate[] getAcceptedIssuers() {
					return new X509Certificate[0];
				}
				@Override
				public void checkClientTrusted(X509Certificate[] certs, String authType) {
				}
				@Override
				public void checkServerTrusted(X509Certificate[] certs, String authType) {
				}
			}
		};
		try {
			SSLContext context = SSLContext.getInstance("SSL");
			context.init(null, trustAllCerts, new SecureRandom());
			HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
		} catch (Exception e) {
		}
	}
}